#include "getimage.h"
#include <QUrl>
#include <QHttpRequestHeader>
#include <QHttpResponseHeader>
#include <QHttp>
#include <QDir>
#include <QDateTime>
#include <QCryptographicHash>
#include <QDebug>

GetImage::GetImage(QObject *parent) :
    QObject(parent)
{
    http = new QHttp();
    currentCacheDir = "";

    connect(http, SIGNAL(requestFinished(int,bool)), this, SLOT(requestFinished(int,bool)));
}

void GetImage::MakeRequest(const QString &url,const QString &modify_time,const QString &etag)
{
    qDebug() << "GetImage::MakeRequest()";
    QUrl _url = QUrl(url);

    QHttpRequestHeader header = QHttpRequestHeader("GET", _url.path(), 1, 1);
        header.setValue("HOST", _url.host());
        header.setValue("Keep-Alive", "300");
        header.setValue("Connection", "keep-alive");
        if(!modify_time.isEmpty())
            header.setValue("If-Modified-Since", modify_time);
        if(!etag.isEmpty())
            header.setValue("If-None-Match", "\""+etag+"\"");

    QBuffer *buffer = new QBuffer();
        buffer->open(QIODevice::ReadWrite);

    int id;
    http->setHost(_url.host());
    id = http->request(header,0, buffer);
    data = qMakePair(id, buffer);
}

void GetImage::requestFinished(int id, bool error)
{
    if(error)
    {
        theNext();
        return;
    }

    if(data.first == id)
    {
        qDebug() << "GetImage::requestFinished: " << id;
        QHttpResponseHeader resp_header = http->lastResponse();
        if(resp_header.isValid())
        {
            responseHeaderReceived(resp_header);
        }
    }
}

void GetImage::responseHeaderReceived(const QHttpResponseHeader &resp_header)
{
    QString etag = resp_header.value("Etag").remove("\"");
    QString screen_name = userDatalist.first().first;

    switch(resp_header.statusCode())
    {
        case GetImage::OK:
        {
            QPixmap image;
            QByteArray imagedata = data.second->data();
            QString modify_time = resp_header.value("Last-Modified");

            if(image.loadFromData(imagedata))
            {
                emit imageReady(screen_name, image);

                QString md5 = QCryptographicHash::hash(imagedata, QCryptographicHash::Md5).toHex();
                if(etag.isEmpty())
                    etag = md5;

                saveImageToCache(imagedata, modify_time, etag, md5);
            } else {
                qDebug() << "GetImage::responseHeaderReceived: wrong image reseived";
                emit error(GetImage::WRONG_IMAGEDATA);
            }
        break;
        }
        case GetImage::NOT_MODIFIED:
        {

        }
        break;
        case GetImage::BAD_REQUEST:

        break;
        case GetImage::NOT_AUTHORIZED:

        break;
        case GetImage::FORBIDDEN:

        break;
        case GetImage::NOT_FOUND:

        break;
        case GetImage::INTERNAL_SERVER_ERROR:

        break;
        case GetImage::BAD_GATEWAY:

        break;
        case GetImage::SERVICE_UNAVAILABLE:

        break;
        default:
        break;
    }
    theNext();
}

void GetImage::theNext()
{
    qDebug() << "GetImage::theNext():";
    if(data.second)
    {
        data.second->close();
        delete data.second;
        data.second = NULL;
    }

    userDatalist.takeFirst();

    if(!userDatalist.isEmpty())
        prepareRequest();
}

int GetImage::saveImageToCache(QByteArray &imagedata,QString &modify_time, QString &etag, QString &md5)
{
    qDebug() << "GetImage::saveImageToCache()";

    CacheData *cacheData = new CacheData();
        cacheData->screen_name = userDatalist.first().first;
        cacheData->modify_time = modify_time;
        cacheData->etag = etag;
        cacheData->md5 = md5;

    //Save image to cache DIR
    if(!QFile::exists(currentCacheDir + "/" + md5))
    {
        QFile image_file(currentCacheDir + "/" + md5);
        if(!image_file.open(QIODevice::WriteOnly))
        {
            qDebug() << "GetImage::saveImageToChache(): cant create file to write image";
            emit error(GetImage::CANT_CREATE_IMAGE_FILE);
            return 1;
        }
        image_file.write(imagedata);
        image_file.close();
    }

    //Add image data to cache file
    QFile cache_file(currentCacheDir + "/" + DEFAULT_CACHE_DATA_FILE);
        if(!cache_file.open(QIODevice::Append))
        {
            qDebug() << "GetImage::saveImageToChache(): cant write to cache data file";
            emit error(GetImage::CANT_CREATE_CACHE_FILE);
            return 1;
        }
    QString data_line = cacheData->screen_name+"|"+cacheData->modify_time+"|"+cacheData->etag+"|"+cacheData->md5+"\n";
    cache_file.write(data_line.toUtf8());
    cache_file.close();

    //Append to current cache in RAM
    cacheDataList.append(cacheData);

    return 0;
}

void GetImage::SetCacheDir(QString cachedir)
{
    qDebug() << "GetImage::SetCacheDir()";
    QDir dir(cachedir);
        if(!dir.exists())
            if(!dir.mkpath(cachedir))
            {
                emit error(GetImage::CANT_CREATE_DIR);
                return;
            }

    currentCacheDir = cachedir;
    qDebug() << "GetImage::SetCacheDir(): " << currentCacheDir;
    loadCache(dir);
}

void GetImage::loadCache(QDir &dir)
{
    QFile cache_file(dir.absolutePath() +"/"+ DEFAULT_CACHE_DATA_FILE);
        if(cache_file.exists())
        {
            if(!cache_file.open(QIODevice::ReadOnly))
            {
                emit error(GetImage::CANT_READ_CACHE_DATA);
                return;
            }

            QRegExp rxFileLine("(.*)\\|(.*)\\|(.*)\\|(.*)");

            while(!cache_file.atEnd())
            {
                QString cache_file_line = cache_file.readLine();
                rxFileLine.indexIn(cache_file_line.trimmed());

                CacheData *cacheData = new CacheData();
                cacheData->screen_name = rxFileLine.cap(1);
                cacheData->modify_time = rxFileLine.cap(2);
                cacheData->etag = rxFileLine.cap(3);
                cacheData->md5 = rxFileLine.cap(4);

                cacheDataList.append(cacheData);

                qDebug() << "GetImage::loadCache(): " << cacheData->screen_name << ":" << cacheData->modify_time << ":" << cacheData->etag <<":"<<cacheData->md5;
            }
            cache_file.close();
        }
}

void GetImage::AddImageData(QString screen_name, QString url)
{
    qDebug() << "GetImage::AddImageData()";
    if(currentCacheDir.isEmpty())
    {
        emit error(GetImage::CACHE_DIR_NOT_SET);
        return;
    }
    userDatalist.append(qMakePair(screen_name, url));
    findCachedFile(screen_name);
    if(userDatalist.size() == 1)
        prepareRequest();
}

void GetImage::findCachedFile(QString &screen_name)
{
    qDebug() << "GetImage::findCachedFile()";
    for(int i = 0; i < cacheDataList.size(); ++i)
    {
        CacheData *cacheData = cacheDataList.at(i);
        if(cacheData->screen_name == screen_name)
        {
            qDebug() << "GetImage::findCachedFile(): load file from cache";
            QPixmap image(currentCacheDir + "/" + cacheData->md5);
            emit imageReady(screen_name, image);
            return;
        }
    }
}

void GetImage::prepareRequest()
{
    qDebug() << "GetImage::prepareRequest()";
    for(int i = 0; i < cacheDataList.size(); ++i)
    {
        CacheData *cacheData = cacheDataList.at(i);
        QString screen_name = userDatalist.first().first;
        if(cacheData->screen_name == screen_name)
        {
            QPixmap image(currentCacheDir + "/" + cacheData->md5);
            emit imageReady(screen_name, image);
            MakeRequest(userDatalist.first().second, cacheData->modify_time, cacheData->etag);
            return;
        }
    }
    MakeRequest(userDatalist.first().second);
}

GetImage::~GetImage()
{

}
